package org.pp.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.pp.annotations.SearchField;
import org.pp.core.BizExcept;
import org.pp.core.PageBean;
import org.pp.core.Result;
import org.pp.dto.PageDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * 增删改查 基类
 *
 * @author rob jiang Coding something meaningful
 * @date 2022/12/7 21:27
 */

@Slf4j
public abstract class BaseCrudController<T, A, E, P> implements PageLoader<P> {

    @Autowired
    IService<T> service;

    private Class<T> clz;
    private Class<P> pgClz;

    private Class<T> getClz() {
        if (this.clz == null) {
            Type actualTypeArgument = ((ParameterizedType) this.getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[0];
            this.clz = (Class<T>) actualTypeArgument;
        }
        return this.clz;
    }

    private Class<P> getPgClz() {
        if (this.pgClz == null) {
            Type actualTypeArgument = ((ParameterizedType) this.getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[3];
            this.pgClz = (Class<P>) actualTypeArgument;
        }
        return this.pgClz;
    }


    protected Serializable getTableId(Object obj) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.getAnnotation(TableId.class) != null) {
                PropertyDescriptor propertyDescriptor = null;
                try {
                    propertyDescriptor = new PropertyDescriptor(field.getName(), obj.getClass());
                    Method getter = propertyDescriptor.getReadMethod();
                    Object value = getter.invoke(obj);
                    return (Serializable) value;
                } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

            }
        }
        throw BizExcept.erro("未声明Id字段");
    }

    public Result<Object> add(@RequestBody @Validated A dto) {
        T t = BeanUtil.copyProperties(dto, getClz());
        service.save(t);
        return Result.ok(getTableId(t));
    }


    public Result<Void> delete(@RequestParam String ids) {
        List<String> list = Arrays.asList(ids.split(","));
        if (!list.isEmpty()) {
            service.removeBatchByIds(list);
        }
        return Result.ok();
    }


    public Result<Void> edite(@RequestBody @Validated E dto) {
        T t = BeanUtil.copyProperties(dto, getClz());
        T obj = service.getById(getTableId(dto));
        if (obj == null) {
            throw BizExcept.erro("id错误");
        }
        service.updateById(t);
        return Result.ok();
    }


    public Result<P> detail(@RequestParam(name = "id", required = true) Long id) {
        T obj = service.getById(id);
        if (obj == null) {
            throw BizExcept.erro("id错误");
        }
        return Result.ok(BeanUtil.copyProperties(obj, getPgClz()));
    }


    public Result<PageBean<P>> page(PageDto page) {
        if (page.isPageShow()) {
            PageHelper.startPage(page.getPageNum(), page.getPageSize());
        }
        QueryWrapper<T> query = new QueryWrapper<>();
        sort(query);

        List<Field> searchFields = Arrays.stream(page.getClass().getDeclaredFields())
                .filter(e -> e.isAnnotationPresent(SearchField.class)).toList();
        for (Field searchField : searchFields) {
            SearchField annotation = searchField.getAnnotation(SearchField.class);
            String fieldName = annotation.value();
            String pattern = annotation.pattern();
            Object fieldvalue = BeanUtil.getFieldValue(page, searchField.getName());
            String operator = annotation.operator();
            boolean orJoin = annotation.orJoin();
            if (nullValue(fieldvalue)) {
                continue;
            }
            //值不满足正则规则，不作为查询条件
            if (fieldvalue instanceof String &&
                    (StrUtil.isNotBlank(pattern) && !ReUtil.isMatch(pattern, (String) fieldvalue))) {
                continue;
            }

            /**
             * 参考 SearchField类
             */
            Consumer<QueryWrapper<T>> c = null;
            switch (operator) {
                //query.or(e-> e.eq(fieldName,fieldvalue))
                case "=" -> c = e -> e.eq(fieldName, fieldvalue);
                case ">" -> c = e -> e.gt(fieldName, fieldvalue);
                case ">=" -> c = e -> e.ge(fieldName, fieldvalue);
                case "<" -> c = e -> e.lt(fieldName, fieldvalue);
                case "<=" -> c = e -> e.le(fieldName, fieldvalue);
                case "!=" -> c = e -> e.ne(fieldName, fieldvalue);
                case "%" -> c = e -> e.like(fieldName, fieldvalue);
                case "%_" -> c = e -> e.likeLeft(fieldName, fieldvalue);
                case "_%" -> c = e -> e.likeRight(fieldName, fieldvalue);
                case "~" -> c = e -> e.in(fieldName, fieldvalue);
            }
            if (orJoin) {
                query.or(c);
            } else {
                query.and(c);
            }
        }

        List<T> list = service.list(query);
        PageInfo<T> pageInfo = new PageInfo<>(list);
        return Result.ok(load(pageInfo,getPgClz()));
    }

    private void sort(QueryWrapper<T> query) {
        List<Field> sortFields = Arrays.stream(getClz().getDeclaredFields())
                .filter(e -> e.isAnnotationPresent(TableId.class)).toList();

        for (Field sortField : sortFields) {
            String fieldName = sortField.getAnnotation(TableId.class).value();
            query.orderByDesc(fieldName);
        }
    }

    private static boolean nullValue(Object fieldvalue) {
        if (Objects.isNull(fieldvalue)) {
            return true;
        }
        if (fieldvalue instanceof String && StrUtil.isBlank((String) fieldvalue)) {
            return true;
        }
        if (fieldvalue instanceof Collection<?> && CollectionUtil.isEmpty((Collection<?>) fieldvalue)) {
            return true;
        }
        if (fieldvalue.getClass().isArray() && ArrayUtil.isEmpty(fieldvalue)) {
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("aaa", "bbb");
        Consumer<String> c = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        list.forEach(c);
    }

    public void handle(List<String> list, Consumer<String> c) {
        for (String s : list) {
            c.accept(s);
        }
    }
}
